﻿using System.Runtime.CompilerServices;
using System.Runtime.Serialization;

namespace HelenServer.Core;

public class OperationResult<T> : OperationResult
{
    protected OperationResult() : base() { }

    public OperationResult(
        OperationStatus status,
        string? message,
        List<FailedHistory> failedHistory,
        T data) : base(status, message, failedHistory)
    {
        Data = data;
    }

    public T Data { get; set; } = default!;

    public static OperationResult<T> OK(T data)
    {
        return OK(data, "");
    }

    public static OperationResult<T> OK(
        T data, string message, params object[] args)
    {
        if (args.Length > 0)
        {
            message = string.Format(message, args);
        }

        return new OperationResult<T>()
        {
            Status = OperationStatus.Success,
            Message = message,
            Data = data
        };
    }

    public static new OperationResult<T> Failed(
        string message,
        [CallerLineNumber] int sourceLineNumber = 0,
        [CallerFilePath] string sourceFilePath = "",
        [CallerMemberName] string memberName = "",
        params object[] args)
    {
        if (args.Length > 0)
        {
            message = string.Format(message, args);
        }

        var result = new OperationResult<T>()
        {
            Status = OperationStatus.Failed,
            Message = message,
        };

        result.FailedHistory.Add(new()
        {
            Caller = new Caller(memberName, sourceFilePath, sourceLineNumber),
            Message = message
        });

        return result;
    }

    public static new OperationResult<T> Error(Exception exception, string? message = null)
    {
        var result = new OperationResult<T>()
        {
            Status = OperationStatus.Error,
#if DEBUG
            Message = exception.ToString(),
#else
            Message = message ?? exception.Message,
#endif
        };

        result.FailedHistory.Add(new()
        {
            Message = message ?? exception.Message,
            Exception = new(exception)
        });

        return result;
    }
}


public class OperationResult
{
    protected OperationResult() { }

    public OperationResult(
        OperationStatus status,
        string? message,
        List<FailedHistory> failedHistory)
    {
        Status = status;
        Message = message ?? string.Empty;
        FailedHistory = failedHistory;
    }

    public OperationStatus Status { get; protected set; } = OperationStatus.Success;

    public string Message { get; protected set; } = string.Empty;

    #region Extensions
    [JsonIgnore]
    public List<FailedHistory> FailedHistory { get; protected set; } = new();
    public bool Succeed => Status == OperationStatus.Success;
    #endregion

    public static OperationResult OK(string message = "", params object[] args)
    {
        if (args.Length > 0)
        {
            message = string.Format(message, args);
        }

        return new OperationResult()
        {
            Status = OperationStatus.Success,
            Message = message
        };
    }

    public static OperationResult Failed(
        string message,
        [CallerLineNumber] int sourceLineNumber = 0,
        [CallerFilePath] string sourceFilePath = "",
        [CallerMemberName] string memberName = "",
        params object[] args)
    {
        if (args.Length > 0)
        {
            message = string.Format(message, args);
        }

        var result = new OperationResult()
        {
            Status = OperationStatus.Failed,
            Message = message,

        };

        result.FailedHistory.Add(new()
        {
            Caller = new Caller(memberName, sourceFilePath, sourceLineNumber),
            Message = message
        });

        return result;
    }

    public static OperationResult Error(Exception exception, string? message = null)
    {
        var result = new OperationResult()
        {
            Status = OperationStatus.Error,
#if DEBUG
            Message = exception.ToString(),
#else
            Message = message?? exception.Message,
#endif
        };

        result.FailedHistory.Add(new()
        {
            Message = message ?? exception.Message,
            Exception = new(exception)
        });

        return result;
    }

    public static OperationResult AuthoryFailed(
    string message,
    [CallerLineNumber] int sourceLineNumber = 0,
    [CallerFilePath] string sourceFilePath = "",
    [CallerMemberName] string memberName = "",
    params object[] args)
    {
        if (args.Length > 0)
        {
            message = string.Format(message, args);
        }

        var result = new OperationResult()
        {
            Status = OperationStatus.AuthoryFailed,
            Message = message,
        };

        result.FailedHistory.Add(new()
        {
            Caller = new Caller(memberName, sourceFilePath, sourceLineNumber),
            Message = message
        });

        return result;
    }
}

[DataContract]
public struct Caller
{
    public Caller(
        string memberName,
        string filePath,
        int lineNumber)
    {
        MemberName = memberName;
        FilePath = filePath;
        LineNumber = lineNumber;
    }

    [DataMember(Order = 1)]
    public string MemberName { get; }

    [DataMember(Order = 2)]
    public string FilePath { get; }

    [DataMember(Order = 3)]
    public int LineNumber { get; }
}

[DataContract]
public class FailedHistory
{
    [DataMember(Order = 1)]
    public CustomException? Exception { get; set; }
    [DataMember(Order = 2)]
    public Caller? Caller { get; set; }
    [DataMember(Order = 3)]
    public string? Message { get; set; }
}

[DataContract]
public class CustomException
{
    public CustomException() { }
    public CustomException(Exception exception)
    {
        Message = exception.Message;
        StackTrace = exception.StackTrace;

        if (exception.InnerException is not null)
        {
            InnerException = new(exception.InnerException);
        }
    }

    [DataMember(Order = 1)]
    public string Message { get; set; } = string.Empty;

    [DataMember(Order = 2)]
    public string? StackTrace { get; set; }

    [DataMember(Order = 3)]
    public CustomException? InnerException { get; set; }
}